//
//  API_AsyncSequence_NotificationVC.swift
//  APSwift
//
//  Created by jianbo.pei on 2024/8/16.
//

import UIKit
import UserNotifications

class API_AsyncSequence_NotificationVC: UIViewController {
    
    // UI 组件
    private lazy var postButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("发送通知", for: .normal)
        button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
        button.backgroundColor = UIColor.systemBlue
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 8
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    private lazy var statusLabel: UILabel = {
        let label = UILabel()
        label.text = "等待通知..."
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 16)
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private lazy var logTextView: UITextView = {
        let textView = UITextView()
        textView.isEditable = false
        textView.font = UIFont.monospacedSystemFont(ofSize: 14, weight: .regular)
        textView.backgroundColor = UIColor.systemGray6
        textView.layer.cornerRadius = 8
        textView.translatesAutoresizingMaskIntoConstraints = false
        return textView
    }()
    
    // 通知监听任务
    private var notificationTask: Task<Void, Never>?
    private var lifecycleTask: Task<Void, Never>?
    private var postCount = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        setupNotificationStream()
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // 取消异步任务
        notificationTask?.cancel()
        lifecycleTask?.cancel()
    }
    
    private func setupUI() {
        view.backgroundColor = .white
        title = "通知 AsyncSequence 示例"
        
        view.addSubview(postButton)
        view.addSubview(statusLabel)
        view.addSubview(logTextView)
        
        NSLayoutConstraint.activate([
            postButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            postButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 50),
            postButton.widthAnchor.constraint(equalToConstant: 200),
            postButton.heightAnchor.constraint(equalToConstant: 50),
            
            statusLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            statusLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            statusLabel.topAnchor.constraint(equalTo: postButton.bottomAnchor, constant: 30),
            
            logTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            logTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            logTextView.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 20),
            logTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
        ])
        
        // 添加按钮事件
        postButton.addTarget(self, action: #selector(postNotification), for: .touchUpInside)
    }
    
    private func setupNotificationStream() {
        // 创建通知流，监听自定义通知
        let notificationStream = NotificationAsyncSequence(
            name: Notification.Name("CustomNotification")
        )
        
        // 开始监听通知
        notificationTask = Task { [weak self] in
            guard let self = self else { return }
            
            for await notification in notificationStream {
                // 检查任务是否被取消
                if Task.isCancelled { break }
                
                await MainActor.run {
                    self.handleNotification(notification)
                }
            }
        }
        
        // 同时监听应用生命周期通知
        let lifecycleStream = NotificationAsyncSequence(
            name: UIApplication.didBecomeActiveNotification
        )
        
        lifecycleTask = Task { [weak self] in
            guard let self = self else { return }
            
            for await notification in lifecycleStream {
                if Task.isCancelled { break }
                
                await MainActor.run {
                    self.appendLog("应用变为活跃状态: \(Date())")
                }
            }
        }
    }
    
    private func handleNotification(_ notification: SendableNotificationData) {
        let timestamp = DateFormatter.localizedString(from: notification.timestamp, dateStyle: .none, timeStyle: .medium)
        
        statusLabel.text = "收到通知: \(notification.name)"
        
        var logMessage = "[\(timestamp)] 通知: \(notification.name)"
        
        // 处理通知中的用户信息
        if !notification.userInfo.isEmpty {
            let userInfoString = notification.userInfo.map { "\($0.key): \($0.value)" }.joined(separator: ", ")
            logMessage += " - 数据: {\(userInfoString)}"
        }
        
        appendLog(logMessage)
    }
    
    private func appendLog(_ message: String) {
        logTextView.text += message + "\n"
        
        // 滚动到底部
        let bottom = NSMakeRange(logTextView.text.count - 1, 1)
        logTextView.scrollRangeToVisible(bottom)
    }
    
    @objc private func postNotification() {
        postCount += 1
        
        let userInfo: [String: Any] = [
            "count": postCount,
            "timestamp": Date(),
            "source": "手动发送"
        ]
        
        NotificationCenter.default.post(
            name: Notification.Name("CustomNotification"),
            object: self,
            userInfo: userInfo
        )
        
        appendLog("发送了第 \(postCount) 个通知")
    }
}

// MARK: - Sendable 通知数据
struct SendableNotificationData: Sendable {
    let name: String
    let userInfo: [String: String]  // 简化为 Sendable 类型
    let timestamp: Date
    
    init(from notification: Notification) {
        self.name = notification.name.rawValue
        self.timestamp = Date()
        
        // 安全地转换 userInfo
        var sendableUserInfo: [String: String] = [:]
        if let userInfo = notification.userInfo {
            for (key, value) in userInfo {
                if let stringKey = key as? String {
                    sendableUserInfo[stringKey] = String(describing: value)
                }
            }
        }
        self.userInfo = sendableUserInfo
    }
}

// MARK: - 通知异步序列
struct NotificationAsyncSequence: AsyncSequence {
    typealias Element = SendableNotificationData  // 改为 Sendable 类型
    
    private let notificationName: Notification.Name
    private let object: AnyObject?
    
    init(name: Notification.Name, object: AnyObject? = nil) {
        self.notificationName = name
        self.object = object
    }
    
    func makeAsyncIterator() -> NotificationAsyncIterator {
        return NotificationAsyncIterator(name: notificationName, object: object)
    }
}

// MARK: - 通知异步迭代器
struct NotificationAsyncIterator: AsyncIteratorProtocol {
    typealias Element = SendableNotificationData  // 改为 Sendable 类型
    
    private let stream: AsyncStream<SendableNotificationData>
    private var iterator: AsyncStream<SendableNotificationData>.Iterator
    
    init(name: Notification.Name, object: AnyObject?) {
        var continuation: AsyncStream<SendableNotificationData>.Continuation!
        
        self.stream = AsyncStream<SendableNotificationData> { cont in
            continuation = cont
            
            // 注册通知观察者
            let observer = NotificationCenter.default.addObserver(
                forName: name,
                object: object,
                queue: nil
            ) { notification in
                // 转换为 Sendable 数据
                let sendableData = SendableNotificationData(from: notification)
                continuation.yield(sendableData)
            }
            
            // 当流结束时移除观察者
            cont.onTermination = { _ in
                NotificationCenter.default.removeObserver(observer)
            }
        }
        
        self.iterator = stream.makeAsyncIterator()
    }
    
    mutating func next() async -> SendableNotificationData? {
        return await iterator.next()
    }
}
